/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package webhook

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"knative.dev/pkg/apis"
	"knative.dev/pkg/logging"
	"knative.dev/serving/pkg/apis/serving"
	v1 "knative.dev/serving/pkg/apis/serving/v1"
)

// ValidateService runs extra validation on Service resources
func ValidateService(ctx context.Context, uns *unstructured.Unstructured) error {
	return validateRevisionTemplate(ctx, uns)
}

// ValidateConfiguration runs extra validation on Configuration resources
func ValidateConfiguration(ctx context.Context, uns *unstructured.Unstructured) error {
	// If owned by a service, skip validation for Configuration.
	if uns.GetLabels()[serving.ServiceLabelKey] != "" {
		return nil
	}

	return validateRevisionTemplate(ctx, uns)
}

func validateRevisionTemplate(ctx context.Context, uns *unstructured.Unstructured) error {
	content := uns.UnstructuredContent()

	// Allow dry-run if specified in request (dry-run=server)
	if !apis.IsDryRun(ctx) {
		return nil
	}

	namespace := uns.GetNamespace()

	// Decode and validate the RevisionTemplateSpec
	val, found, err := unstructured.NestedFieldNoCopy(content, "spec", "template")
	if err != nil {
		return fmt.Errorf("could not traverse nested spec.template field: %w", err)
	}
	if !found {
		logger := logging.FromContext(ctx)
		logger.Warn("no spec.template found for unstructured")
		return nil
	}

	templ, err := decodeTemplate(val)
	if err != nil {
		return err
	}
	if templ == nil || templ == (&v1.RevisionTemplateSpec{}) {
		return nil // Don't need to validate empty templates
	}

	return validatePodSpec(ctx, templ.Spec, namespace)
}
